package net.solvetheriddle.wwclient.activity;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;

import net.solvetheriddle.wwclient.R;
import net.solvetheriddle.wwclient.WWAlarmReciever;
import net.solvetheriddle.wwclient.WWLogic;
import net.solvetheriddle.wwclient.WWParser;
import net.solvetheriddle.wwclient.database.WWDatabaseManager;
import net.solvetheriddle.wwclient.entity.Project;
import net.solvetheriddle.wwclient.entity.Work;
import android.app.Activity;
import android.app.AlarmManager;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.PendingIntent;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.SystemClock;
import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

public class ProjectListActivity extends Activity {

  private static final String WWCLIENT = "WWCLIENT";
  private static final long ALARM_DELAY = 600000l; // 3600000l = one hour in
  // millis
  protected static final int WORK_TABLE_TAB = 1;
  private static final int MENU_START_WORK = Menu.FIRST;
  // private static final int MENU_END_WORK = Menu.FIRST + 1;
  public static final int ALARM_DIALOG = 0;

  // UI widgets
  private ListView projectLV;
  private TextView hintTV;
  private TextView workInProgressTV;
  private Button endWorkButton;
  private EditText descriptionET;

  private MainActivity mainActivity;
  public static AlarmManager alarm;
  private AlertDialog.Builder aBuilder;
  private PendingIntent alarmIntent;
  private List<Project> projectList;
  private ArrayAdapter<Project> projectListAdapter;
  private Project selectedProject;

  private WWDatabaseManager db;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    try {
      super.onCreate(savedInstanceState);

      // init database
      db = new WWDatabaseManager(getApplicationContext());

      // init UI
      setContentView(R.layout.projectlist);
      setupViews();

      // setup the screen - do not change the ORDER of method calls
      checkForUnfinishedWork();
      setupScreen();
      setupListeners();
      setupAlarm();

      // show dialog if the activity was called from a notification
      Bundle extras = getIntent().getExtras();
      if (extras != null && extras.getBoolean(WWAlarmReciever.CALLED_BY_ALARM)) {
        // show dialog which can possibly END current work
        showDialog(ALARM_DIALOG);
      }

      // **** Test purposes *****
      // insertNewWorkManually();

      // load projects and works from internet, update database
      new UpdateProjectsTask().execute();
//      asyncTasksInMethod();

    } catch (Exception e) {
      // Log exception with the WWCLIENT tag
      Log.e(WWCLIENT, "! ERROR IN CODE: " + e.toString());
      e.printStackTrace();
    }
  }

  private void insertNewWorkManually() {
    db.open();
    db.addWork(new Work(2, 3717, "test work", WWLogic.getCurrentMillis() - WWParser.ONE_HOUR_IN_MILLIS, WWLogic.getCurrentMillis()));
    db.close();
  }

  @Override
  protected void onResume() {
    // projectListAdapter.notifyDataSetInvalidated();
    super.onResume();
  }

  @Override
  public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
    super.onCreateContextMenu(menu, v, menuInfo);
    menu.add(0, MENU_START_WORK, Menu.NONE, getString(R.string.startWork));
  }

  @Override
  public boolean onContextItemSelected(MenuItem item) {
    switch (item.getItemId()) {
    case (MENU_START_WORK):
      AdapterView.AdapterContextMenuInfo menuInfo = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
      selectedProject = (Project) projectLV.getItemAtPosition(menuInfo.position);
      selectedProject.setActive(true);
      startWork(selectedProject);
      break;
    }
    return super.onContextItemSelected(item);
  }

  @Override
  protected Dialog onCreateDialog(int id) {
    switch (id) {
    case ALARM_DIALOG:
      aBuilder = new AlertDialog.Builder(ProjectListActivity.this);
      aBuilder.setView(descriptionET);
      aBuilder.setTitle(getString(R.string.descriptionDialogLabel));
      aBuilder.setMessage(getString(R.string.descriptionDialogText));
      aBuilder.setPositiveButton(getString(R.string.saveAndContinue), new android.content.DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int which) {
          Project activeProject = saveWorkAndContinue(descriptionET.getText().toString());
          Toast.makeText(getApplicationContext(), getString(R.string.savedContinue) + " " + activeProject.getName(), Toast.LENGTH_LONG).show();
        }
      });
      aBuilder.setNegativeButton(getString(R.string.saveAndEnd), new android.content.DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int which) {
          saveWorkAndEnd(descriptionET.getText().toString());
          Toast.makeText(getApplicationContext(), getString(R.string.savedEnd), Toast.LENGTH_SHORT).show();
        }
      });
      aBuilder.setCancelable(true);
      aBuilder.setOnCancelListener(new OnCancelListener() {
        public void onCancel(DialogInterface dialog) {
          Toast.makeText(getApplicationContext(), getString(R.string.continueWork), Toast.LENGTH_SHORT).show();
        }
      });
      return aBuilder.create();
    }
    return null;
  }
  
  /**
   * @param description Text description of the work.
   * @return The project the work is assigned to.
   */
  protected Project saveWorkAndContinue(String description) {
    long projectID = saveWorkAndEnd(description);
    db.open();
    Project activeProject = db.getProject(projectID);
    db.close();
    startWork(activeProject);
    return activeProject;
  }

  /**
   * Ends current work setting it the given <code>description</code> and sends
   * it to the server.
   * 
   * @param description Text description of the finished work.
   * @return ID of project the work is assigned to.
   */
  protected long saveWorkAndEnd(String description) {
    long projectId = endWorkUpdateDb(description);
    
    // reset current work in WWLogic
    WWLogic.finishCurrentWork();
    alarm.cancel(alarmIntent);
    updateUI();

    db.open();
    // TODO Toast
    Log.d(WWCLIENT, "*** Ending work on project " + db.getProject(projectId).getName());
    db.close();
    return projectId;
  }

  /**
   * Checks if there is any unfinished work first. If so, Toast is show
   * informating user he must end his current work first. If there is none, it
   * starts a new work on <code>selectedProject</code>.
   * 
   * @param selectedProject one of open projects on which the user wants to work
   */
  protected void startWork(Project selectedProject) {
    if (WWLogic.workInProgress()) {
      Toast.makeText(getApplicationContext(), getString(R.string.endCurrentWorkFirst), Toast.LENGTH_LONG).show();
      return;
    } else {
      // start new work
      Work startedWork = startWorkUpdateDb(selectedProject.getId());
      // set current work in WWLogic
      WWLogic.setCurrentWork(startedWork);
      alarm.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + ALARM_DELAY, ALARM_DELAY, alarmIntent);
      updateUI();
      switchParentTab(WORK_TABLE_TAB);
      
      Toast.makeText(getApplicationContext(), getString(R.string.beginWorkOn) + " " + selectedProject.getName(), Toast.LENGTH_LONG).show();
      Log.d(WWCLIENT, "*** Starting new work on project " + selectedProject);
    }
  }

  /**
   * @param project
   * @return Started work.
   * 
   */
  private Work startWorkUpdateDb(long projectId) {
    Work newWork = new Work(projectId, WWLogic.getCurrentMillis());
    db.open();
    long newWorkId = db.addNewWork(newWork);
    db.close();
    newWork.setId(newWorkId);
    return newWork;
  }

  /**
   * @param description Text description of ended work.
   * @return ID of ended and saved work.
   */
  private long endWorkUpdateDb(String description) {
    db.open();
    Work workToBeEnded = db.getUnfinishedWork();
    workToBeEnded.setDescription(description); // TODO posefit null
    workToBeEnded.setTo(WWLogic.getCurrentMillis());
    db.updateWork(workToBeEnded);
    db.close();
    return workToBeEnded.getProjectID();
  }

  /**
   * SET WWLogic.CurrentWork
   */
  private void checkForUnfinishedWork() {
    db.open();
    Work unfinishedWork = db.getUnfinishedWork();
    db.close();
    WWLogic.setCurrentWork(unfinishedWork);
  }
  
  /**
   * Updates UI according to the current working state.
   */
  private void updateUI() {
    if(WWLogic.workInProgress()) {
      hintTV.setText(R.string.beginWorkOn);
      projectLV.setVisibility(View.GONE);
      workInProgressTV.setVisibility(View.VISIBLE);
      endWorkButton.setVisibility(View.VISIBLE);
      mainActivity.triggerWorkingLabel();
    } else {
      hintTV.setText(R.string.projectListHint);
      projectLV.setVisibility(View.VISIBLE);
      workInProgressTV.setVisibility(View.GONE);
      endWorkButton.setVisibility(View.GONE);
      mainActivity.triggerWorkingLabel();
    }
    
  }

  /**
   * Switches the tabs to the screen of given tabIndex. Use one of:
   * <ul>
   * <li>TAB_PROJECTLIST - Tab containing the list of projects</li>
   * <li>TAB_WORK_OVERVIEW - Tab containing todays work overview</li>
   * <li>TAB_STATISTICS - Tab containing various statistics</li>
   * </ul>
   * 
   * @param tabIndex index of the screen you want to switch to
   */
  public void switchParentTab(int tabIndex) {
    MainActivity parentActivity = (MainActivity) this.getParent();
    parentActivity.switchTab(tabIndex);
  }

  /**
   * Set up alarm to periodical notifications.
   */
  private void setupAlarm() {
    alarm = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
    alarmIntent = PendingIntent.getBroadcast(this, 0, new Intent(net.solvetheriddle.wwclient.WWAlarmReciever.ACTION_WWALARM), Intent.FLAG_GRANT_READ_URI_PERMISSION);
  }

  /**
   * Either populates project list from database or shows overview information
   * about current work.
   */
  private void setupScreen() {
    Work currentWork = WWLogic.getCurrentWork();
    setWorkInProgressScreen(currentWork);
    setProjectListScreen();
    updateUI();
  }

  /**
   * Show project list, set it up. Hide work overview information.
   */
  private void setProjectListScreen() {
    db.open();
    projectList = db.getAllProjects();
    db.close();
    projectListAdapter = new ArrayAdapter<Project>(this, android.R.layout.simple_list_item_1, projectList);
    projectLV.setAdapter(projectListAdapter);
    registerForContextMenu(projectLV);
    if (projectList.isEmpty()) {
      hintTV.setText(getString(R.string.noProjects));
    }
  }

  /**
   * Show overview information about currentWork. Hide project list.
   * 
   * @param currentWork
   */
  private void setWorkInProgressScreen(Work currentWork) {
    // FIXME nezobrazuje se spravne
    if(currentWork != null) {
      StringBuilder workInProgressText = new StringBuilder();
      workInProgressText.append("\n\nPráce na zakázce:\n");
      db.open();
      workInProgressText.append(db.getProject(currentWork.getProjectID())).append("\n");
      db.close();
      workInProgressText.append("Zde uvidite, jak dlouho na ni pracujete");
      workInProgressTV.setText(workInProgressText.toString());
    }
  }

  /**
   * Set up all Listeners.
   */
  private void setupListeners() {
    endWorkButton.setOnClickListener(new OnClickListener() {
      public void onClick(View v) {
        // show dialog which can possibly END current work
        showDialog(ALARM_DIALOG);
      }
    });

    projectLV.setOnItemClickListener(new OnItemClickListener() {
      public void onItemClick(AdapterView<?> adapter, View selectedView, int position, long arg3) {
        selectedProject = (Project) adapter.getItemAtPosition(position);
        startWork(selectedProject);
      }
    });
  }

  /**
   * Get reference to UI widgets
   */
  private void setupViews() {
    projectLV = (ListView) findViewById(R.id.projectListView);
    hintTV = (TextView) findViewById(R.id.hintTextView);
    workInProgressTV = (TextView) findViewById(R.id.workInProgressTextView);
    endWorkButton = (Button) findViewById(R.id.endWorkButton);
    descriptionET = new EditText(this);
    mainActivity = (MainActivity) this.getParent();
  }
  
  

  private class UpdateProjectsTask extends AsyncTask<Void, Void, List<Project>> {

    // Load resources from UI Thread to this Asynchronous Task
    @Override
    protected void onPreExecute() {
      super.onPreExecute();

      // TODO .setVisibility(ProgressBar.VISIBLE);
    }

    // Run this code in background. Do not reference UI Thread here!
    @Override
    protected List<Project> doInBackground(Void... params) {
      try {
        return WWLogic.loadProjectsFromServer();
      } catch (IOException e) {
        // TODO vyhodit toast (nekde mimo tuto metodu)
        Log.e(WWCLIENT, "! Error while loading projects. Problem with connection.");
        e.printStackTrace();
      }
      return Collections.unmodifiableList(new ArrayList<Project>());
    }

    // Update UI according to result from doInBackground() method
    @Override
    protected void onPostExecute(List<Project> result) {
      // TODO .setVisibility(ProgressBar.VISIBLE);

      // update database
      if (!result.isEmpty()) {
        db.open();
        db.deleteAllProjects();
        for (Project project : result) {
          db.addProject(project);
        }
        db.close();
        // update UI
        updateProjects();
      }

      new UploadFinishedWorks().execute();

      super.onPostExecute(result);
    }
  }
  
  /**
   * Populates active projects from databse to an existing projectListAdapter.
   */
  private void updateProjects() {
    projectListAdapter.clear();
    db.open();
    for (Project p : db.getAllProjects()) {
      projectListAdapter.add(p);
    }
    db.close();
    projectListAdapter.notifyDataSetChanged();
    // TODO pridat toast
    // Toast.makeText(getApplicationContext(),
    // getString(R.string.serverError) + " " +
    // WWLogic.getActiveProject().getName(), Toast.LENGTH_LONG).show();
  }

  private class UploadFinishedWorks extends AsyncTask<Void, Integer, List<Work>> {

    List<Work> notSentWorks;

    // Load resources from UI Thread to this Asynchronous Task
    @Override
    protected void onPreExecute() {
      super.onPreExecute();
      db.open();
      notSentWorks = db.getNotSentWorks();
      db.close();
      // TODO .setVisibility(ProgressBar.VISIBLE);
    }

    // Run this code in background. Do not reference UI Thread here!
    @Override
    protected List<Work> doInBackground(Void... params) {
      try {
        if (notSentWorks != null) {
          int count = notSentWorks.size();
          for (int i = 0; i < count; i++) {
            Work notSentWork = notSentWorks.get(i);
            WWLogic.sendFinishedWorkToServer(notSentWork);
            int progress = (int) (i / (float) count) * 100;
            publishProgress((int) notSentWork.getId(), progress);
          }
        }
      } catch (IOException e) {
        // TODO vyhodit toast (nekde mimo tuto metodu)
        Log.e(WWCLIENT, "! Error while loading works. Problem with connection.");
        e.printStackTrace();
      }
      return Collections.unmodifiableList(new ArrayList<Work>());
    }

    // Runs on the UI thread
    @Override
    protected void onProgressUpdate(Integer... progress) {
      db.open();
      if (db.deleteWork(progress[0]) != 0) {
        // TODO update progress bar
        // setProgressPercent(progress[1]);
        Log.d(WWCLIENT, "* Work with ID: " + progress[0] + " deleted.");
      } else {
        Log.d(WWCLIENT, "! Work with ID: " + progress[0] + " was NOT deleted.");
      }
      db.close();
      super.onProgressUpdate(progress);
    }

    // Update UI according to result from doInBackground() method
    @Override
    protected void onPostExecute(List<Work> result) {
      // TODO .setVisibility(ProgressBar.VISIBLE);
      // TODO update UI - vyhodit toast, informovat uzivatele

      new UpdateWorksTask().execute();

      super.onPostExecute(result);
    }
  }

  private class UpdateWorksTask extends AsyncTask<Void, Void, List<Work>> {

    // Load resources from UI Thread to this Asynchronous Task
    @Override
    protected void onPreExecute() {
      super.onPreExecute();

      // TODO .setVisibility(ProgressBar.VISIBLE);
    }

    // Run this code in background. Do not reference UI Thread here!
    @Override
    protected List<Work> doInBackground(Void... params) {
      try {
        return WWLogic.loadWorkFromServer(ourEpoch(), WWLogic.getCurrentMillis());
      } catch (IOException e) {
        // TODO vyhodit toast (nekde mimo tuto metodu)
        Log.e(WWCLIENT, "! Error while loading works. Problem with connection.");
        e.printStackTrace();
      }
      return Collections.unmodifiableList(new ArrayList<Work>());
    }

    private long ourEpoch() {
      Date ourEpoch = new Date();
      ourEpoch.setMonth(7);
      ourEpoch.setDate(1);
      ourEpoch.setHours(0);
      ourEpoch.setMinutes(0);
      return ourEpoch.getTime();
    }

    // Update UI according to result from doInBackground() method
    @Override
    protected void onPostExecute(List<Work> result) {
      // TODO .setVisibility(ProgressBar.VISIBLE);

      db.open();
      // update database
      if (!result.isEmpty()) {
        db.deleteAllWorks();
        for (Work work : result) {
          Log.d(WWCLIENT, "@ RECIEVED from server: " + work);
          db.addWork(work);
        }
        // TODO update UI - vyhodit toast, informovat uzivatele
      }
      db.close();
      super.onPostExecute(result);
    }
  }
}
